/*
 * TextFile.java
 *
 *  created: 21.8.2011
 *  charset: UTF-8
 *  license: MIT (X11) (See LICENSE file for full license)
 */
package cz.mp.k3bg.core;

import cz.mp.k3bg.Application;
import cz.mp.k3bg.log.LoggerManager;
import cz.mp.util.FileUtils;
import cz.mp.util.StringUtils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.logging.Logger;


/**
 * Třída {@code TextFile} je abstrakcí textového souboru a základová 
 * třída pro jednotlivé typy textových souborů.
 * <p>
 * Kromě informace o kódování znaků zachovává také informaci o zadané cestě.
 * Obsahuje další pomocné funkce (jako: {@linkplain #readFileToString()},
 * {@linkplain #getFileBaseName()}, {@linkplain #getFileBasePath()}, 
 * {@linkplain #getFileExtension()}).
 * <p>
 * Informace o kódování znaků není detekována automaticky, je třeba ji zadat.
 * 
 * 
 * @author Martin Pokorný
 * @version 0.1
 */
public class TextFile {
    
    private static final boolean DEBUG = false;
    private static final Logger logger =
            LoggerManager.getLogger(TextFile.class, DEBUG);
    
    
    protected File file;
    
    protected Charset charset;
    
    /** Cesta k souboru, muže být relativní i absolutní. */
    protected String path;

    // -----
    
    /**
     * 
     * @param path
     * @param charsetName 
     */
    public TextFile(String path, String charsetName) {
        setPathImpl(path);
        setCharsetImpl(Charset.forName(charsetName));
    }
    
    /**
     * Nová instance; jako kódování textu je zvoleno 
     * {@linkplain Application#DEFAULT_CHARSET}.
     * 
     * @param path
     */
    public TextFile(String path) {
        this(path, Application.DEFAULT_CHARSET);
    }

    
    // -----
    
    /**
     * 
     * Voláno konstruktorem.
     * 
     * @param path 
     */
    private void setPathImpl(String path) {
        if (StringUtils.isBlank(path)) {
            throw new IllegalArgumentException("path is empty");
        }
        logger.finer("path = " + path);
        this.path = path;
        this.file = new File(path);        
    }
    
    public String getPath() {
        return path;
    }
    
    /**
     * 
     * @param toDir
     * @return 
     */
    public String getRelativePath(String toDir) {
        if (file.isAbsolute()) {
            return FileUtils.relativizePath(toDir, getPath());
        }
        else {
            return getPath();
        }
    }
    
    public File getFile() {
        return file;
    }

    public String getAbsolutePath() {
        return FileUtils.getAbsoluteFilePath(file);
//        return file.getAbsolutePath();
    }
    
//    public String getCanonicalPath() throws IOException {
//        return file.getCanonicalPath();
//    }
    
    /**
     * 
     * @return 
     */
    public boolean exists() {
        return file.exists() && ! file.isDirectory();
    }
    
    /**
     * 
     * @return 
     */
    public boolean delete() {
        return file.delete();
    }    
    
    /**
     * 
     * @return 
     */
    public Charset getCharset() {
        return charset;
    }

    /**
     * 
     * @param charset 
     */
    public void setCharset(Charset charset) {
        setCharsetImpl(charset);
    }
    
    public String getCharsetName() {
        return charset.name();
    }
        
    public void setCharsetName(String charsetName) {
        setCharsetImpl(Charset.forName(charsetName));
    }
    
    /** 
     * 
     * Voláno konstruktorem.
     * 
     * @param charset 
     */
    private void setCharsetImpl(Charset charset) {
        if (charset == null) {
            throw new IllegalArgumentException("charset=null");
        }    
        logger.finer("charset = " + charset.name());
        this.charset = charset;
    }
    
    
    /**
     * Získá příponu souboru.
     * <p>
     * např:
     * <pre>
     * {@literal /home/martin/file.txt --> txt}
     * </pre>
     * 
     * @return přípona souboru (bez znaku {@literal .})
     */    
    public String getFileExtension() {
        return FileUtils.getFileExtension(file);
    } 

    
    /**
     * Získá základní jméno souboru.
     * <p> 
     * např:
     * <pre>
     * {@literal /home/martin/file.txt --> file}
     * </pre>
     * 
     * @return 
     */
    public String getFileBaseName() {
        return FileUtils.getFilePathWithoutExt(file.getName());
    }
    
    /**
     * Získá jméno souboru i s cestou k němu.
     * <p> 
     * např:
     * <pre>
     * {@literal /home/martin/file.txt --> /home/martin/file}
     * </pre>
     * 
     * @return 
     */
    public String getFileBasePath() {
        return FileUtils.getFilePathWithoutExt(path);
    }

    
    /**
     * 
     * @return
     * @throws UnsupportedEncodingException
     * @throws FileNotFoundException 
     */
    public BufferedWriter getBufferedWriter() throws FileNotFoundException {
        return getBufferedWriter(false);
    }
    
    /**
     * 
     * 
     * @param append  zda se mají při zápisu do souboru data připojit na konec
     * @return
     * @throws UnsupportedEncodingException
     * @throws FileNotFoundException 
     */
    public BufferedWriter getBufferedWriter(boolean append) 
            throws FileNotFoundException {
        BufferedWriter writer = new BufferedWriter(
                new OutputStreamWriter(
                    new FileOutputStream(file, append), charset ));
        return writer;
    }
    
    /**
     * 
     * @return
     * @throws FileNotFoundException 
     */
    public BufferedReader getBufferedReader() throws FileNotFoundException {        
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(
                    new FileInputStream(file), charset ));
        return reader;
    }

    /**
     * Načte textový soubor a uloží ho do textového řetězce, který vrátí.
     * 
     * @param file
     * @param charset
     * @return
     * @throws IOException 
     */
    public String readFileToString() throws IOException {
        if (file == null) {
            throw new IllegalStateException("no file");
        }
        logger.fine(file.getName());

        // S FileChannel a MappedByteBuffer 
        //  Ne kvůli  Java Bug ID: 4715154 -
        //  Soubor nelze smazat, pokud je namapovaný v paměti
        //  "Windows does not allow a mapped file to be deleted."
//        FileInputStream stream = new FileInputStream(file);
//        FileChannel fc = null;
//        try {
//            // viz Java Bug ID: 4715154
//            fc = stream.getChannel();
//            MappedByteBuffer bb = fc.map(
//                    FileChannel.MapMode.READ_ONLY, 0, fc.size());            
//            return charset.decode(bb).toString();
//        } finally {
//            if (fc != null) {                
//                fc.close();
//            }
//            stream.close();            
//        }
        
        StringBuilder sb = new StringBuilder(10000);
        
        BufferedReader reader = getBufferedReader();
        String line = "";
        while((line = reader.readLine()) != null) {
            sb.append(line).append(Application.EOL);
        }
        
        reader.close();
        
        return sb.toString();
    }    

    /**
     * Textová reprezentace. 
     * 
     * @return  {@linkplain #getPath() }
     */
    @Override
    public String toString() {
        return getPath();
    }
    
}   // TextFile
